﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using LModBusRTU;
using System.Timers;
using ZLSerialPortBusLib.PLCManager;
using System.Threading;
using HCLSMod;
using System.Diagnostics;

namespace ZLSerialPortBusLib.SerialPortBus
{

    public delegate void TMBRecResponDatasCallBack(object sender, byte[] fRecDatas);

    public enum TSerialRSCON
    {
        None = 0,
        RTS = 1,
        DTR = 1,
    }

    /// <summary>
    /// 串口收发数据管理器，该模块管理底层数据，不管理Modbus命令
    /// 1、创建对象后，线程首先创建串口对象，但使用本控件前，依然必须先打开指定端口，
    ///    成功开端口后才能够正常收发数据；
    /// 2、可以通过事件：OnRecResponDatas，处理收到的应答数据；
    /// 3、发送命令后，通过连续检查RecResponDatas（外部属性，在发送数据时，强制设为空值）
    ///    是否有新的数据，从而判断是否收到应答；
    /// 4、CRCEndian控制CRC大小头；
    /// 5、SysCOMPortList为当前系统中串口列表；
    /// 6、常量RecTimeOutMs为接收应答数据最大超时；超过改时长，强制发生超时；
    /// 7、内置线程fSerialPortRunThd主要管理是否收到应答数据；
    /// 8、内置线程fRxdCheckTimeOutThd主要控制收到应答数据的超时；
    /// 9、程序退出的时候，必须使用CloseAll函数，释放资源，关闭线程，否则无法全部关闭；
    /// 10、变量ThdActive控制线程是否运行，实现最后资源释放，初始化时必须最先设置为True；
    /// 
    /// 使用：
    /// 1、创建对象；
    /// 2、启动串口；
    /// 方法一：配置参数：PortName，BaudRate，CRCEndian，RSConPORT；设置Connected=true;
    /// 方法二：配置参数：CRCEndian，RSConPORT；使用OpenCOMPort函数，打开指定串口；
    /// 3、通过IsOpen或Connected属性，判断是否已经打开串口；
    /// 4、通过StopSerialPort函数关闭串口，但不清场，不关闭守护线程；
    /// 5、通过CloseAll函数关闭串口清场，会关闭守护线程；该函数可以继承重载；
    /// </summary>
    public class ZLSerialPortBase : ZLErrorRecord
    {
        const bool RS485RxMode = true;
        const bool RS485TxMode = !RS485RxMode;

        public event SystemEventLog OnEventLog = null;
        /// <summary>
        /// 收到应答数据事件
        /// </summary>
        public event TMBRecResponDatasCallBack OnRecResponDatas;
        /// <summary>
        /// 系统串口列表
        /// </summary>
        public static string[] SysCOMPortList { get { return SerialPort.GetPortNames(); } }
        /// <summary>
        /// 接受到数据后下一数据超时
        /// </summary>
        public const int RecTimeOutMs = 100;

        #region 成员

        #region 外部使用属性
        /// <summary>
        /// 串口是否已经连接，如果已经设置好串口参数，也可启动串口
        /// </summary>
        public bool Connected
        {
            get { return IsOpen; }
            set
            {
                if (fSerialPort1 != null)
                {
                    if (value)
                    {
                        if ((!string.IsNullOrEmpty(fSerialPort1.PortName))
                            && (fSerialPort1.BaudRate > 0))
                        {
                            fSerialPort1.Open();
                        }
                    }
                    else
                        fSerialPort1.Close();
                }
            }
        }
        /// <summary>
        /// 设置CRC大小头设置
        /// </summary>
        public TCRC16Endian CRCEndian { get; set; }
        /// <summary>
        /// 设置RS485的RSCON使用RTS还是DTR
        /// </summary>
        public TSerialRSCON RSConPORT { get; set; }
        /// <summary>
        /// 串口端口名称
        /// </summary>
        public string PortName
        {
            get { return (fSerialPort1 != null) ? fSerialPort1.PortName : ""; }
            set
            {
                if (fSerialPort1 != null)
                    fSerialPort1.PortName = value;
            }
        }
        /// <summary>
        /// 串口速度
        /// </summary>
        public int BaudRate
        {
            get { return (fSerialPort1 != null) ? fSerialPort1.BaudRate : 0; }
            set
            {
                if (fSerialPort1 != null)
                    fSerialPort1.BaudRate = value;
            }
        }      
        
        /// <summary>
        /// 串口状态描述字符串，未开启则返回"Closed!",开启则返回:串口名称，速度，RS485端口
        /// </summary>
        public string SerialPortInfo
        {
            get
            {
                string fResult = "Closed!";
                if ((fSerialPort1 != null) && (fSerialPort1.IsOpen))
                    fResult = string.Format("{0},{1},{2},{3}",
                        fSerialPort1.PortName,
                        fSerialPort1.BaudRate.ToString(),
                        CRCEndian.ToString(),
                        this.RSConPORT.ToString());
                return fResult;
            }
        }
        /// <summary>
        /// 串口是否成功连接
        /// </summary>
        public bool IsOpen { get { return (fSerialPort1 != null) && fSerialPort1.IsOpen && ThdActive; } }

        #endregion
        /// <summary>
        /// 接收数据超时计数器
        /// </summary>
        protected int fRxdTick { get; set; }
        /// <summary>
        /// 接收数据缓冲区
        /// </summary>
        protected List<byte> fRecBuf { get; set; }
        /// <summary>
        /// 串口控件
        /// </summary>
        protected SerialPort fSerialPort1 { get; set; } = new SerialPort();
        /// <summary>
        /// 外部调用串口，谨慎使用
        /// </summary>
        public SerialPort COMPort { get { return fSerialPort1; } }
        /// <summary>
        /// 收到的应答数据，当收到数据后，放在该处，可以通过检查该成员判断是否收到应答
        /// 当发送数据时，自动强制设置为空值
        /// </summary>
        protected byte[] fRecResponDatas = null;
        /// <summary>
        /// 收到的应答数据，当收到数据后，放在该处，可以通过检查该成员判断是否收到应答
        /// 当发送数据时，自动强制设置为空值
        /// </summary>
        public byte[] RecResponDatas { get { return fRecResponDatas; } }
        /// <summary>
        /// 模块主线程
        /// </summary>
        protected Thread fSerialPortRunThd { get; set; }
        /// <summary>
        /// 模块线程是否运行标识，关闭程序时必须设为false
        /// </summary>
        protected bool ThdActive { get; set; }
        /// <summary>
        /// 检查接收数据超时线程
        /// </summary>
        protected Thread fRxdCheckTimeOutThd { get; set; }

        #region 发送接收数据记录功能
        /// <summary>
        /// 线程锁字符串
        /// </summary>
        private const string fUniquKey = "ZLMBLib_2018_SerialPortBus_RTDataList";
        /// <summary>
        /// 线程锁
        /// </summary>
        private Mutex fThdMutexInstance { get; set; }
        /// <summary>
        /// 已经发送数据信息列表，用于记录发送数据
        /// </summary>
        protected List<ZLByteDataRecord> fTxDataList = new List<ZLByteDataRecord>();

        public int TxDataCount { get { return fTxDataList.Count; } }
        // 提取最后发送的数据，提取后删除记录
        public ZLByteDataRecord LastTxData
        {
            get
            {
                if (fTxDataList.Count>0)
                {
                    ZLByteDataRecord fTxData = fTxDataList[fTxDataList.Count - 1];
                    fTxDataList.RemoveAt(fTxDataList.Count - 1);
                    return fTxData;
                }
                return null;
            }
        }
        /// <summary>
        /// 已经接收数据信息列表，用于记录接收数据
        /// </summary>
        protected List<ZLByteDataRecord> fRxDataList = new List<ZLByteDataRecord>();
        public int RxDataCount { get { return fRxDataList.Count; } }

        // 提取最后接收的数据，提取后删除记录
        public ZLByteDataRecord LastRxData
        {
            get
            {
                if (fRxDataList.Count > 0)
                {
                    ZLByteDataRecord fRxData = fRxDataList[fRxDataList.Count - 1];
                    fRxDataList.RemoveAt(fRxDataList.Count - 1);
                    return fRxData;
                }
                return null;
            }
        }
        #endregion

        #endregion

        #region 构建函数、线程及内部函数
        public ZLSerialPortBase()
        {
            CRCEndian = TCRC16Endian.Big;
            RSConPORT = TSerialRSCON.None;
            fRecBuf = new List<byte>();
            ThdActive = true;
            fSerialPortRunThd = new Thread(MainRunThdMethod);
            fSerialPortRunThd.Start();
            fRxdCheckTimeOutThd = new Thread(CheckTimeOutThdMethod);
            fRxdCheckTimeOutThd.Start();
            fRxdTick = 0;
            fThdMutexInstance = new Mutex(false, fUniquKey);
        }
        /// <summary>
        /// 主线程：检查是否收到数据
        /// </summary>
        /// <param name="parameter"></param>
        protected void MainRunThdMethod(object parameter)
        {
            while (ThdActive)
            {
                if (fSerialPort1 == null)
                    fSerialPort1 = new SerialPort();
                else
                {
                    try
                    {
                        if (fSerialPort1.IsOpen)
                            while (fSerialPort1.IsOpen && (fSerialPort1.BytesToRead > 0))
                            {
                                if (!ThdActive)
                                    break;
                                else
                                {
                                    fRxdTick = 1;
                                    while(fSerialPort1.BytesToRead>0)
                                        fRecBuf.Add((byte)fSerialPort1.ReadByte());
                                    // 创建独立的缓冲区，保存数据，保证线程安全
                                    byte[] tBufFull = fRecBuf.ToArray();

                                    if ((tBufFull.Length>2)&&CheckRecDatas(tBufFull))
                                    {
                                        // 清除CRC后的数据
                                        byte[] tBufNoCRC = new byte[tBufFull.Length - 2];
                                        for (int i = 0; i < tBufNoCRC.Length; i++)
                                            tBufNoCRC[i] = tBufFull[i];

                                        if (OnRecResponDatas != null)
                                            OnRecResponDatas(this, tBufNoCRC);
                                        /*
                                        }
                                        if (CheckRecDatas(tBuf, ref fRecResponDatas)
                                            // 检查收到的数据是对应的应答
                                            && (RecResponDatas != null) && (RecResponDatas.Length > 0))
                                            // 清除CRC后的数据正确
                                        {
                                            if (OnRecResponDatas != null)
                                                OnRecResponDatas(this, RecResponDatas);
                                            */
                                        // 记录收到的数据
                                        fThdMutexInstance.WaitOne();
                                        try
                                        {
                                            fRxDataList.Add(new ZLByteDataRecord(fRecBuf.ToArray()));
                                            if (fRxDataList.Count > 1000)
                                                fRxDataList.RemoveAt(0);
                                        }
                                        finally
                                        {
                                            fThdMutexInstance.ReleaseMutex();
                                            fRecBuf.Clear();
                                        }
                                        fRxdTick = 0;
                                    }
                                }
                                Thread.Sleep(1);
                            }
                    }
                    catch (Exception ex)
                    {
                        fRecBuf.Clear();
                        PutErrorEvent(ex);
                        if (OnEventLog != null)
                            OnEventLog(this, EventLogEntryType.Error, ex.Message);
                    }
                }
                Thread.Sleep(1);
            }
            fSerialPort1.Close();
            fSerialPort1 = null;
        }
        /// <summary>
        /// 监控收到数据超时线程
        /// </summary>
        /// <param name="parameter"></param>
        protected void CheckTimeOutThdMethod(object parameter)
        {
            while (ThdActive)
            {
                Thread.Sleep(1);
                if ((fRxdTick > 0) && (fRxdTick < 0x7FFF))
                    fRxdTick++;
                if (fRxdTick > RecTimeOutMs)
                {
                    fRecBuf.Clear();
                    fRxdTick = 0;
                }
            }
        }

        /// <summary>
        /// 检查收到的数据符合Modbus协议的应答，检查通过表示已经收到完整的数据帧
        /// 由于使用了ref标志，会直接操作调用源的缓冲空间，造成线程不安全
        /// </summary>
        /// <param name="fDatas">收到的数据</param>
        /// <param name="fRecDatas">返回收到的数据，不包括CRC</param>
        /// <returns>符合返回True</returns>
        protected bool CheckRecDatas(byte[] fDatas, ref byte[] fRecDatas)
        {
            bool fResult = false;
            fRecDatas = null;
            if ((fDatas != null) && (fDatas.Length >= 5))
            {
                int fiCRC = CRC16_GB19582.CRC16GB(fDatas, fDatas.Length - 2, CRCEndian);

                byte fD0 = fDatas[fDatas.Length - 2];
                byte fD1 = fDatas[fDatas.Length - 1];
                byte fRB = (byte)((fiCRC >> 8) & 0xFF);
                byte fRL = (byte)(fiCRC & 0xFF);
                bool fCRCCheck = false;

                if ((fD0 == fRB) && (fD1 == fRL))
                    fCRCCheck = true;

                if (fCRCCheck)
                {
                    // 清除CRC
                    fRecDatas = new byte[fDatas.Length - 2];
                    for (int i = 0; i < fDatas.Length - 2; i++)
                        fRecDatas[i] = fDatas[i];
                    return true;// TModbusRTUCommandV2.CheckResponseComplete(fRecDatas);
                }
            }
            return fResult;
        }
        /// <summary>
        /// 不在干涉其他内存空间，保证线程安全
        /// </summary>
        /// <param name="fDatas"></param>
        /// <returns></returns>
        protected bool CheckRecDatas(byte[] fDatas)
        {
            bool fResult = false;
            if ((fDatas != null) && (fDatas.Length >= 5))
            {
                int fiCRC = CRC16_GB19582.CRC16GB(fDatas, fDatas.Length - 2, CRCEndian);

                byte fD0 = fDatas[fDatas.Length - 2];
                byte fD1 = fDatas[fDatas.Length - 1];
                byte fRB = (byte)((fiCRC >> 8) & 0xFF);
                byte fRL = (byte)(fiCRC & 0xFF);

                if ((fD0 == fRB) && (fD1 == fRL))
                    fResult = true;
            }
            return fResult;
        }
        /// <summary>
        /// 设置RS485单工模式下的发送或接收模式
        /// </summary>
        /// <param name="fSendMode">开启发送模式？</param>
        protected void fSetRSCONStatus(bool fSendMode)
        {
            if (this.RSConPORT == TSerialRSCON.DTR)
                fSerialPort1.DtrEnable = fSendMode;
            if (this.RSConPORT == TSerialRSCON.RTS)
                fSerialPort1.RtsEnable = fSendMode;
        }
        #endregion

        #region 外部功能函数
        /// <summary>
        /// 向串口发送数据
        /// </summary>
        /// <param name="fDatas">等待发送的数据，不包括CRC</param>
        /// <returns>成功发送时返回发送的数据数量，失败返回-1；</returns>
        public int SendData(byte[] fDatas)
        {
            int fResult = -1;
            if (IsOpen && (fDatas != null) && (fDatas.Length > 0))
            {
                fRecBuf.Clear();
                fRxdTick = 0;
                List<byte> fSendBuf = new List<byte>(fDatas);
                int fCRC16 = CRC16_GB19582.CRC16GB(fSendBuf.ToArray(), CRCEndian);
                fSendBuf.Add((byte)(fCRC16 >> 8));
                fSendBuf.Add((byte)(fCRC16 & 0xFF));

                try
                {
                    if (RSConPORT != TSerialRSCON.None)
                    {
                        fSetRSCONStatus(RS485TxMode);              // 切换到RS485的发送模式
                        System.Threading.Thread.Sleep(5);          // 必须最少休眠1ms，保证切换时间 
                        // 如果使用了隔离芯片，休眠时间最少5ms
                    }
                    fRecResponDatas = null;
                    fSerialPort1.Write(fSendBuf.ToArray(), 0, fSendBuf.Count);
                    while (fSerialPort1.BytesToWrite > 0)
                        System.Threading.Thread.Sleep(1);
                    // 记录发送的数据
                    fThdMutexInstance.WaitOne();
                    try
                    {
                        ZLByteDataRecord fTxData = new ZLByteDataRecord(fSendBuf.ToArray());
                        fTxDataList.Add(fTxData);
                        if (fTxDataList.Count > 1000)
                            fTxDataList.RemoveAt(0);
                    }
                    finally
                    {
                        fThdMutexInstance.ReleaseMutex();
                        fRecBuf.Clear();
                    }
                    fResult = fSendBuf.Count;
                    if (RSConPORT != TSerialRSCON.None)
                    {
                        System.Threading.Thread.Sleep(5);         // 必须休眠1ms，保证切换时间
                        fSetRSCONStatus(RS485RxMode);             // 切换到RS485的接收模式
                    }
                    fRecBuf.Clear();
                }
                catch (Exception ex)
                {
                    PutErrorEvent(ex);
                    if (OnEventLog != null)
                        OnEventLog(this, EventLogEntryType.Error, ex.Message);
                }
            }
            return fResult;
        }
        /// <summary>
        /// 开启串口
        /// </summary>
        /// <param name="fPortName"></param>
        /// <param name="fBandrate"></param>
        /// <returns></returns>
        public bool OpenCOMPort(string fPortName, int fBaudrate)
        {
            if ((fSerialPort1 != null) && (!fSerialPort1.IsOpen))
            {
                fSerialPort1.PortName = fPortName;
                fSerialPort1.BaudRate = fBaudrate;
                fSerialPort1.Open();
                fSetRSCONStatus(RS485RxMode);          // 直接启动RS485的接收模式
            }
            return IsOpen;
        }

        public bool OpenCOMPort(string fPortName, int fBaudrate, TSerialRSCON fRSCON)
        {
            RSConPORT = fRSCON;
            return OpenCOMPort(fPortName, fBaudrate);
        }
        /// <summary>
        /// 关闭串口，并退出线程，用于程序关闭情场
        /// </summary>
        /// <returns></returns>
        public virtual bool CloseAll()
        {
            Disconnect();
            ThdActive = false;
            Connected = false;
            return IsOpen;
        }
        /// <summary>
        /// 关闭串口，不退出线程
        /// 已经废弃，不在使用
        /// </summary>
        /// <returns></returns>
        public bool StopSerialPortNoUse()
        {
            Disconnect();
            Connected = false;
            return IsOpen;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <returns></returns>
        public bool Disconnect()
        {
            if (fSerialPort1 != null)
                fSerialPort1.Close();
            Connected = false;
            return IsOpen;
        }

        /// <summary>
        /// 提取收到的数据帧，提取后会删除记录
        /// </summary>
        /// <param name="fRecData"></param>
        /// <returns>提取的数据帧长度，未提取到为0</returns>
        public int GetRxPacketInList(out ZLByteDataRecord fRxData)
        {
            fRxData = null;
            if (fRxDataList.Count > 0)
            {
                fThdMutexInstance.WaitOne();
                try
                {
                    fRxData = fRxDataList[0];
                    fRxDataList.RemoveAt(0);
                }
                finally
                {
                    fThdMutexInstance.ReleaseMutex();
                }
                if (fRxData != null)
                    return 1;
            }
            return 0;
        }
        /// <summary>
        /// 提取发送的数据帧，提取后会删除记录
        /// </summary>
        /// <param name="fSendData"></param>
        /// <returns>提取的数据帧长度，未提取到为0</returns>
        public int GetTxPacketInList(out ZLByteDataRecord fTxData)
        {
            fTxData = null;
            if (fRxDataList.Count > 0)
            {
                fThdMutexInstance.WaitOne();
                try
                {
                    fTxData = fTxDataList[0];
                    fTxDataList.RemoveAt(0);
                }
                finally
                {
                    fThdMutexInstance.ReleaseMutex();
                }
                if (fTxData != null)
                    return 1;
            }
            return 0;
        }

        #endregion
    }


    /// <summary>
    /// 串口总线管理模块，该模块管理对象为Modbus命令，命令收到应答后的处理由Modbus命令中携带的事件处理；
    /// 1、系统关闭时，必须调用CloseAll()函数，关闭线程，释放资源
    /// 2、当命令执行完成后，不管是否有正常应答，都调用OnCommandComplete事件，
    ///    但是输入的Modbus命令已经标记是否有应答及应答数据内容；详见Modbus Base；
    /// 3、变量fMBCommandRunning，标记是否有命令正在执行；如果有命令正在执行，将会挂起下达的命令操作；
    /// 4、变量RunCommandListEnable控制是否开始执行命令列表中的命令；
    /// </summary>
    public class ZLSerialPortModbusToolBase : ZLSerialPortBase
    {
        #region 成员
        /// <summary>
        /// 当前等待应答的命令
        /// </summary>
        protected TModbusRTUCommandV2 tMBCommandRunning = null;
        /// <summary>
        /// 命令超时，单位：ms
        /// </summary>
        public int CommandRunTimeOut { get; set; }

        public event TMBCommandResponCallBack OnCommandComplete;
        /// <summary>
        /// 当前等待应答的命令
        /// </summary>
        public TModbusRTUCommandV2 MBCommandRunning { get { return tMBCommandRunning; } }
        /// <summary>
        /// 等待发送的命令的队列
        /// </summary>
        private List<TModbusRTUCommandV2> fMBCommandList = new List<TModbusRTUCommandV2>();
        /// <summary>
        /// 等待发送的命令的队列
        /// </summary>
        public TModbusRTUCommandV2[] MBCommandList { get { return fMBCommandList.ToArray(); } }

        /// <summary>
        /// 等待发送的命令的数量
        /// </summary>
        public int CommandsInListCount { get { return fMBCommandList.Count; } }
        /// <summary>
        /// 上次接受到数据的时间
        /// </summary>
        protected DateTime LastRecDatas { get; set; }

        /// <summary>
        /// 模块主线程
        /// </summary>
        protected Thread fCommandListRunThd { get; set; }
        /// <summary>
        /// 模块线程是否运行标识，关闭程序时必须设为false
        /// </summary>
        protected bool ThdCommandListActive { get; set; }
        public bool RunCommandListEnable { get; set; }
        #endregion

        #region 构建函数及外部功能函数
        public ZLSerialPortModbusToolBase()
        {
            RunCommandListEnable = false;
            CommandRunTimeOut = 50;
            LastRecDatas = DateTime.Now;
        }

        private void ThdCommandListMainRun(object parameter)
        {
            while (ThdCommandListActive)
            {
                TModbusRTUCommandV2 fCommand = null;
                if (RunCommandListEnable && (fMBCommandList.Count > 0))
                {
                    fCommand = fMBCommandList[0];
                    fMBCommandList.RemoveAt(0);
                    int fTimeOut = CommandRunTimeOut;
                    SendData(fCommand, ref fTimeOut);
                }
            }
        }

        /// <summary>
        /// 向串口发送命令，仅发送，不处理应答
        /// </summary>
        /// <param name="fCommand"></param>
        /// <returns></returns>
        public int SendData(TModbusRTUCommandV2 fCommand)
        {
            return SendData(fCommand, 0);
        }
        /// <summary>
        /// 向串口发送命令，在超时时间范围内等待应答
        /// </summary>
        /// <param name="fCommand">发送的命令，同时承载并返回应答的数据</param>
        /// <param name="fResponTimeOut">超时，单位：ms</param>
        /// <returns></returns>
        public int SendData(TModbusRTUCommandV2 fCommand, int fResponTimeOut)
        {
            int fTimeOut = fResponTimeOut;
            return SendData(fCommand, ref fTimeOut);
        }
        /// <summary>
        /// 向串口发送命令，在超时时间范围内等待应答
        /// </summary>
        /// <param name="fCommand">发送的命令，同时承载并返回应答的数据</param>
        /// <param name="fResponTimeOut">超时，完成后返回实际运行时间，单位：ms</param>
        /// <returns>如果未设置超时，成功发送时返回发送的数据数量，失败返回-1；
        ///          如果设置了超时，获得应答则返回应答携带的数据量，超时返回-1；</returns>
        public int SendData(TModbusRTUCommandV2 fCommand, ref int fResponTimeOut)
        {
            int fResult = -2;
            if (IsOpen && (fCommand != null))
            {
                fResult++;
                // 如果发现有命令正在执行，挂起当前命令，等待上一命令执行完；
                while (tMBCommandRunning != null)
                    Thread.Sleep(1);
                // 上一命令已经执行完成
                if (tMBCommandRunning == null)
                {
                    fResult++;
                    byte[] fSendBuf = fCommand.GetPacketDataNoCRC();

                    tMBCommandRunning = fCommand;
                    try
                    {
                        // 命令列表中的命令将无法执行
                        RunCommandListEnable = false;
                        DateTime fLastSendCommand = DateTime.Now;
                        fResult = base.SendData(fSendBuf);
                        // 需要等待应答
                        if (fResponTimeOut > 0)
                        {
                            fResult = 0;
                            TimeSpan fTimePass = DateTime.Now - fLastSendCommand;
                            while (fTimePass.TotalMilliseconds < fResponTimeOut)
                            {
                                Thread.Sleep(1);
                                fTimePass = DateTime.Now - fLastSendCommand;
                                if ((RecResponDatas != null)
                                    && (RecResponDatas.Length > 0))
                                {
                                    fResult = fCommand.CheckReply(RecResponDatas);
                                    fResponTimeOut = (int)fTimePass.TotalMilliseconds;
                                    break;
                                }
                            }
                        }
                    }
                    finally
                    {
                        tMBCommandRunning = null;
                        // 命令列表中的命令可以执行
                        RunCommandListEnable = true;
                    }
                    if (OnCommandComplete != null)
                        OnCommandComplete(fCommand);
                    return fResult;
                }
            }
            return 0;
        }
        
        #endregion

        /// <summary>
        /// 将命令加入到命令列表中
        /// </summary>
        /// <param name="fCommand"></param>
        /// <returns></returns>
        public int AppendModbusCommand(TModbusRTUCommandV2 fCommand)
        {
            fMBCommandList.Add(fCommand);
            return fMBCommandList.Count;
        }
        /// <summary>
        /// 关闭所有功能
        /// 将会调用上级类的CloseAll函数
        /// </summary>
        /// <returns></returns>
        public override bool CloseAll()
        {
            ThdCommandListActive = false;
            return base.CloseAll();
        }
    }
    
}
